#include <iostream>
#include <string>
#include <sstream>
#include <typeinfo>

#include "model/der/ComponentDER.h"
#include "model/der/Attribute.h"
#include "model/der/Entity.h"
#include "model/Utils.h"
#include "model/Diagram.h"
#include "model/Via.h"

ComponentDER::ComponentDER(const std::string & name, int type) {
	setName(name);
	m_type = type;
	m_posX = m_posY = 0;
	m_expression = "";
}


ComponentDER::ComponentDER(const ComponentDER & otroComponente) {
	setName(otroComponente.nombre);
	m_type = otroComponente.m_type;
	m_posX = otroComponente.m_posX;
	m_posY = otroComponente.m_posY;
	m_expression = otroComponente.m_expression;
	componentType = otroComponente.componentType;
}

ComponentDER& ComponentDER::operator=(const ComponentDER& otroComponente){
	setName(otroComponente.nombre);
	m_type = otroComponente.m_type;
	m_posX = otroComponente.m_posX;
	m_posY = otroComponente.m_posY;
	componentType = otroComponente.componentType;

	return *this;
}


ComponentDER::~ComponentDER(){
	for ( unsigned i=0 ; i<conectoresEntrantes.size() ; i++)
		delete conectoresEntrantes[i];
	for ( unsigned i=0 ; i<conectoresSalientes.size() ; i++)
		delete conectoresSalientes[i];
}


void ComponentDER::addConnector(unsigned xRel, unsigned yRel, bool isAttribute, unsigned exitNumber, unsigned int idconector) {
	Connector* conector= new Connector(this, isAttribute, xRel, yRel, exitNumber, idconector);
	isAttribute ? conectoresEntrantes.push_back(conector) :
			conectoresEntrantes.push_back(conector);
}


void ComponentDER::serialize(XmlWriter & writer_rep, XmlWriter & writer_comp)
{
	Utils utils;
	xmlNode* nodoActual;
	writer_rep.addCurrentNodeProperty(TARGET_NAME, nombre.c_str());
	writer_rep.addCurrentNodeProperty(TARGET_TYPE, utils.convertToString(m_type).c_str());
	writer_rep.addCurrentNodeProperty(TARGET_POS_X, utils.convertToString(m_posX).c_str());
	writer_rep.addCurrentNodeProperty(TARGET_POS_Y, utils.convertToString(m_posY).c_str());

	if (nombre != "Via"){
		nodoActual = writer_comp.addCurrentNodeChild("Componente", "");
		writer_comp.addProperty(nodoActual, TARGET_NAME, nombre.c_str());
		writer_comp.setCurrentNode(nodoActual);
	}

}


void ComponentDER::deserialize(XmlReader & reader)
{
	Utils utils;
	nombre = reader.getCurrentNodeProperty(TARGET_NAME);
	m_type = (int) utils.convertToUnsigned(reader.getCurrentNodeProperty(TARGET_TYPE));
	m_posX = utils.convertToUnsigned(reader.getCurrentNodeProperty(TARGET_POS_X));
	m_posY = utils.convertToUnsigned(reader.getCurrentNodeProperty(TARGET_POS_Y));
}

void ComponentDER::deserialize(XmlReader & reader, XmlReader & readerModel)
{
	deserialize(reader);
}

void ComponentDER::validate(std::fstream & filestr, bool &valido){

	std::string aux;
	aux= typeid((*this)).name();

	int cantAtr=0;
	int cantEnt=0;
	int cantForkEnt=0;
	int cantForkNoEnt=0;

	aux.erase(std::remove_if(aux.begin(), aux.end(), &isdigit), aux.end());

	if (  strcmp(aux.c_str(),"Via") != 0 ){
	this->update();
	filestr << " <H1> Nombre: " << this->getName() << " Tipo: " << aux << "</H1>";

	if (  strcmp(aux.c_str(),"Entity") == 0 ){
		if ( m_attributes.empty() && m_entities.empty() ){
			filestr << "<H2>	Error: El elemento esta desconectado" << "</H2>";
			valido = false;
		}
	  for(std::vector<Attribute*>::iterator it = m_attributes.begin(); it != m_attributes.end(); ++it){
			std::string str = typeid((**it)).name();
			str.erase(std::remove_if(str.begin(), str.end(), &isdigit), str.end());
			// verifico que tenga al menos un atributo
			if  ( strcmp(str.c_str(),"Attribute") == 0 )
				cantAtr++;
				
	  }
	     if ( cantAtr == 0){
	    	 filestr << "<H2>	Error: La entidad no posee ningun atributo" << "</H2>";
	    	 valido = false;
	     }
	}

	if (  strcmp(aux.c_str(),"Relation") == 0 ){
		if ( m_attributes.empty() && m_entities.empty() ){
			filestr << "<H2>	Error: El elemento esta desconectado" << "</H2>";
			valido = false;
		}
	      for(std::vector<Entity*>::iterator it = m_entities.begin(); it != m_entities.end(); ++it){
				std::string str = typeid((**it)).name();
				str.erase(std::remove_if(str.begin(), str.end(), &isdigit), str.end());

				// No puede estar unida a otra relacion
				if  ( strcmp(str.c_str(),"Relation") == 0 ){
					filestr << "<H2	Error: Una relacion no puede estar unida a otra relacion ("  <<  this->getName() << "," << (*it)->getName() << ")"  << "</H2>";
					valido = false;
				}
				// Una relacion tiene que estar unida al menos con dos entidades
				if  ( strcmp(str.c_str(),"Entity") == 0 )
					cantEnt++;
			}
		     if ( cantEnt < 2) {
		    	 filestr << "<H2>	Error: Las relaciones deben estar conectadas al menos con 2 entidades" << "</H2>";
		    	 valido = false;
		     }
	}

	if (  strcmp(aux.c_str(),"CompositeAttribute") == 0 ) {
		if ( m_attributes.empty() && m_entities.empty() ){
			filestr << "<H2>	Error: El elemento esta desconectado" << "</H2>";
			valido = false;
		}
	}

	if (  strcmp(aux.c_str(),"Fork") == 0 ) {
		if ( m_attributes.empty() && m_entities.empty() ){
			filestr << "<H2>	Error: El elemento esta desconectado" << "</H2>";
			valido = false;
		}
	}
	if (!m_attributes.empty()) {
			filestr
					<< "<H2>        Error: El Fork no puede tener atributos conectados"
					<< "</H2>";
			valido = false;
		}
		for (std::vector<Entity*>::iterator it = m_entities.begin(); it
				!= m_entities.end(); ++it) {
			std::string str = typeid((**it)).name();
			str.erase(std::remove_if(str.begin(), str.end(), &isdigit),
					str.end());

			if (strcmp(str.c_str(), "Entity") == 0)
				cantForkEnt++;
			else
				cantForkNoEnt++;
		}
		if (cantForkNoEnt != 0) {
			filestr
					<< "<H2>        Error: El Fork tiene elementos conectados que no son entidades"
					<< "</H2>";
			valido = false;
		}
		if (cantForkEnt <= 2) {
			filestr
					<< "<H2>        Error: El Fork tiene que estar conectado al menos a 3 entidades"
					<< "</H2>";
			valido = false;
		}

	if (  strcmp(aux.c_str(),"Attribute") == 0 ) {
		Connector* con = this->conectoresSalientes[0];
		if ( con->getVia() == NULL ) {
			filestr << "<H2>	Error: El elemento esta desconectado" << "</H2>";
			valido = false;
		}
	}
	filestr <<std::endl;
	}

}

Connector *ComponentDER::getConectorAComponente(Component *componenteDestino) {
	Connector *conectorBuscado = NULL;
	for(unsigned i = 0; i < conectoresEntrantes.size(); i++){
		if(conectoresEntrantes[i]->getVia() != NULL) {
			Via* via = conectoresEntrantes[i]->getVia();

			if(via->getEndConnector() != conectoresEntrantes[i]) {
				Component *componenteAlOtroLadoDeLaVia = via->getEndConnector()->getFather();
				if (componenteAlOtroLadoDeLaVia == componenteDestino) {
					conectorBuscado = conectoresEntrantes[i];
				}
			}
		}
	}
	for(unsigned i = 0; i < conectoresSalientes.size(); i++){
		if(conectoresSalientes[i]->getVia() != NULL) {
			Via* via = conectoresSalientes[i]->getVia();

			if(via->getStartConnector() != conectoresSalientes[i]) {
				Component *componenteAlOtroLadoDeLaVia = via->getStartConnector()->getFather();
				if (componenteAlOtroLadoDeLaVia == componenteDestino) {
					conectorBuscado = conectoresSalientes[i];
				}
			}
		}
	}

	return conectorBuscado;
}
